/**
 * 程序运行环境.提供常规的消息通信,界面跳转等.
 * APP,AbsWindow/Popup等对象可以获取Context对象.
 * 不同APP 拿到的Context 不是同一个.需要注意.
 */
import AbsWindow from "./AbsWindow";
import App from "./App";
import DecorView from "./DecorView";
import Messenger from "./handler/Messenger";
import PopupWindow from "./PopupWindow";
import CommonUtil from "./utils/CommonUtil";
import {WindowContent} from "./dialog/WindowContent";
import IPrefable from "./reflect/IPrefable";

export enum UI {
    Window = 0, Popup, DecorView
}

export default class Context {

    public get messenger(): Messenger {
        return this._messenger;
    }

    // 设置自定义win prefab 目录.
    public set winDir(v: string) {
        if (v) {
            this._winDir = v;
            if (this._winDir.charAt(v.length - 1) != "/") {
                this._winDir = this._winDir.concat("/");
            }
        } else {
            console.error("winDir 路径非法.");
        }
    }

    // 设置自定义decor prefab 目录.
    public set decorDir(v: string) {
        if (v) {
            this._decorDir = v;
            if (this._decorDir.charAt(v.length - 1) != "/") {
                this._decorDir = this._decorDir.concat("/");
            }
        } else {
            console.error("decorDir 路径非法.");
        }
    }

    // 设置自定义popup prefab 目录.
    public set popupDir(v: string) {
        if (v) {
            this._popupDir = v;
            if (this._popupDir.charAt(v.length - 1) != "/") {
                this._popupDir = this._popupDir.concat("/");
            }
        } else {
            console.error("winDir 路径非法.");
        }
    }

    public static get(): Context {
        return this._instance;
    }

    private static _instance: Context = null;

    private static generatePrefabPath(name: string, path: string, defPath: string): string {
        if (!name && !path) {
            return "";
        }
        if (!name) {
            return path;
        } else {
            if (!path) {
                return defPath + name + ".prefab";
            } else {
                return path + name + ".prefab";
            }
        }
    }

    public readonly app: App = null;
    private readonly _messenger: Messenger = null;
    private readonly prefCache: { [key: string]: cc.Prefab; } = {};

    private _winDir: string = "prefab/windows/";
    private _decorDir: string = "prefab/decors/";
    private _popupDir: string = "prefab/popups/";

    constructor(app: App) {
        this.app = app;
        this._messenger = new Messenger();
        Context._instance = this;
    }

    // 打开window. 如果当前窗口已存在,则直接显示.可以加一些参数进行传递.
    public async startWindow<T extends AbsWindow>(type: { prototype: T }, args: any = {}): Promise<T> {
        const winMgr = this.app.getContent();
        const index = winMgr.incZIndex();
        const win: T = winMgr.findWindow(type);
        if (win) {
            // 找到window.
            if (!win.isShowing()) {
                win.node.zIndex = index;
            } else {
                // 正在显示中, 不需改变顺序.
            }
            win.setArguments(args);
            win.show();
            return Promise.resolve(win);
        }

        const name = Context.generatePrefabPath(type.prototype.winName, type.prototype.path, this._winDir);
        if (!name) {
            return Promise.reject(`window name is null,make sure u added @WindowName() on type:${type}`);
        }
        // 从cache中查找到prefab资源.
        let prefable = this.prefCache[name];
        if (!prefable) {
            prefable = await CommonUtil.loadResNoError<cc.Prefab>(name);
        }
        if (prefable) {
            this.prefCache[name] = prefable;
            const win = cc.instantiate(prefable);
            const winCtr = win.getComponent(type);
            if (winCtr) {
                winCtr.setup(this.app);
                winCtr.setArguments(args);
                winMgr.addView(winCtr, index);
                win.name = type.prototype.winName;
                return Promise.resolve(winCtr);
            } else {
                return Promise.reject(`AbsWin Comp not found:${name}`);
            }
        } else {
            return Promise.reject(`AbsWin prefab not found:${name}`);
        }
    }

    public async closeWindow<T extends AbsWindow>(type: { prototype: T }) {
        const winMgr = this.app.getContent();
        const win: T = winMgr.findWindow(type);
        if (win) {
            return win.finish();
        }
    }

    // 添加常驻浮层.
    public async attachDecor<T extends DecorView>(type: { prototype: T }, args: any = {}): Promise<T> {
        const winMgr = this.app.getDecorManager();
        const win: T = winMgr.getView(type);
        if (win) {
            // 找到window.
            win.setArguments(args);
            win.show();
            return Promise.resolve(win);
        }
        const name = Context.generatePrefabPath(type.prototype.winName, type.prototype.path, this._decorDir);
        if (!name) {
            return Promise.reject(`decor name is null,make sure u added @WindowName() on type:${type}`);
        }
        // 从cache中查找到prefab资源.
        let prefable = this.prefCache[name];
        if (!prefable) {
            prefable = await CommonUtil.loadResNoError<cc.Prefab>(name);
        }
        if (prefable) {
            this.prefCache[name] = prefable;
            const win = cc.instantiate(prefable);
            const decor = win.getComponent(type);
            if (decor) {
                decor.setup(this.app);
                winMgr.addView(decor);
                decor.setArguments(args);
                win.name = type.prototype.winName;
                decor.show();
                return Promise.resolve(decor);
            } else {
                return Promise.reject(`DecorView Comp not found:${name}`);
            }
        } else {
            return Promise.reject(`DecorView Prefab not found:${name}`);
        }
    }

    // 显示弹窗类型.
    public async popup<T extends PopupWindow>(type: { prototype: T }, forceCreate: boolean = true, args: any = {}): Promise<T> {
        const winMgr = this.app.getPopupManager();
        const win: T = winMgr.getView(type);
        if (win && !forceCreate) {
            win.node.zIndex = winMgr.incZIndex();
            // 找到window.
            win.setArguments(args);
            win.show();
            return Promise.resolve(win);
        }

        const name = Context.generatePrefabPath(type.prototype.winName, type.prototype.path, this._popupDir);
        if (!name) {
            return Promise.reject(`popup name is null,make sure u added @WindowName() on type:${type}`);
        }

        // 从cache中查找到prefab资源.
        let prefable = this.prefCache[name];
        if (!prefable) {
            prefable = await CommonUtil.loadResNoError<cc.Prefab>(name);
        }
        if (prefable) {
            this.prefCache[name] = prefable;
            const win = cc.instantiate(prefable);
            const decor = win.getComponent(type);
            if (decor) {
                decor.setup(this.app);
                winMgr.addView(decor);
                decor.setArguments(args);
                win.name = type.prototype.winName;
                return Promise.resolve(decor);
            } else {
                return Promise.reject(`PopupWindow Comp not found:${name}`);
            }
        } else {
            return Promise.reject(`Popup Prefab ${name} not found`);
        }
    }

    // 关闭悬浮窗.
    public async dettachDecor<T extends DecorView>(type: { prototype: T }): Promise<T> {
        const winMgr = this.app.getDecorManager();
        const win: T = winMgr.getView(type);
        if (win) {
            win.finish();
            return Promise.resolve(win);
        }
        return Promise.resolve(null);
    }

    // 关闭弹窗界面.
    public async closePopup(...type: { prototype: WindowContent }[]): Promise<any> {
        const winMgr = this.app.getPopupManager();
        for (let i of type) {
            const dialog: WindowContent = winMgr.getContentView(i);
            if (dialog) {
                dialog.dismiss();
            }
        }
    }

    // 关闭所有弹窗.
    public async closePopups(): Promise<any> {
        const winMgr = this.app.getPopupManager();
        const dialogs = winMgr.getContentViews();
        for (let dialog of dialogs) {
            dialog.dismiss();
        }
    }

    /**
     *  预加载页面.
     * @param args
     */
    public async preload(...args: { prototype: IPrefable }[]) {
        this.preloadEx(UI.Window, args);
    }

    public async preloadEx(type: UI, args: { prototype: IPrefable }[]) {
        console.time("preload-" + type);
        let defPath = this._winDir;
        switch (type) {
            case UI.Window: {
                defPath = this._winDir;
                break;
            }
            case UI.Popup: {
                defPath = this._popupDir;
                break;
            }
            case UI.DecorView: {
                defPath = this._decorDir;
                break;
            }
        }
        for (let win of args) {
            const name = Context.generatePrefabPath(win.prototype.winName, win.prototype.path, defPath);
            if (!name) {
                continue;
            }
            let prefable = this.prefCache[name];
            if (!prefable) {
                prefable = await CommonUtil.loadResNoError<cc.Prefab>(name, cc.Prefab);
            } else {
                continue;
            }
            this.prefCache[name] = prefable;
        }
        console.timeEnd("preload-" + type);
    }
}
